package edu.hit.stu.config;

import com.auth0.jwt.interfaces.DecodedJWT;
import edu.hit.stu.util.common.IPrint;
import edu.hit.stu.util.jwt.JwtProperties;
import edu.hit.stu.util.jwt.JwtStatus;
import edu.hit.stu.util.jwt.JwtUtil;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AbstractUserDetailsReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 刘智彬
 */
@Component
public class JWTReactiveAuthenticationManager implements ReactiveAuthenticationManager {

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        String token = (String) authentication.getCredentials();
        JwtStatus jwtStatus = JwtUtil.verifyJWT(token);
        switch (jwtStatus) {
            case MATCH:
                Authentication auth = setupSpringAuthentication(token);
                return Mono.just(auth);
            case EXPIRED:
                authenticationFalseHandler(JwtStatus.EXPIRED.getMsg());
                break;
            case NOT_MATCH:
                authenticationFalseHandler(JwtStatus.NOT_MATCH.getMsg());
                break;
            default:
                authenticationFalseHandler("未匹配到jwt状态码");
                break;
        }
        return Mono.empty();
    }

    /**
     * jwt校验 不通过 时调用
     * <ol>
     *   <li>打印返回状态信息</li>
     *   <li>之后清空 SecurityContextHolder</li>
     * </ol>
     * @param msg 返回状态信息
     */
    private void authenticationFalseHandler(String msg) {
        IPrint.printWithColor(msg);
        SecurityContextHolder.clearContext();
    }

    /**
     * jwt 校验成功后调用<br/>设置 authentication → SecurityContextHolder
     * @param token jwt令牌
     */
    private Authentication setupSpringAuthentication(String token) {
        DecodedJWT jwt = JwtUtil.getJWT(token);
        List<String> asList = jwt.getClaim("authorities").asList(String.class);
        List<GrantedAuthority> authorities = asList.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        String username = jwt.getSubject();
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        return authenticationToken;
    }
}
